Dịch chuyển và quay bit Phép_toán_thao_tác_bit

Các phép dịch chuyển bit đôi khi được xem là các phép toán thao tác bit, bởi vì chúng sẽ xem một giá trị dưới dạng một dãy bit hơn là dưới dạng số lượng số (numerial quantity). Trong các phép toán này, các chữ số sẽ được di chuyển, hoặc dịch chuyển, sang trái hoặc phải. Các thanh ghi trong vi xử lý máy tính có độ dài cố định, vì vậy một vài bit sẽ bị "dịch chuyển ra ngoài" thanh ghi ở một đầu, trong khi đó thì một lượng bit tương ứng sẽ được "dịch chuyển vào" ở đầu còn lại; sự khác biệt ở các phép toán dịch chuyển bit nằm ở chỗ cách chúng xác định giá trị của các bit được dịch chuyển vào.

Dịch chuyển số học

Dịch chuyển số học tráiDịch chuyển số học phải

Trong dịch chuyển số học, các bit được dịch chuyển ra khỏi đầu hoặc đuôi sẽ bị loại bỏ. Trong phép dịch chuyển số học về bên trái, các số 0 được dịch chuyển vào bên phải; trong phép dịch chuyển số học bên phải, bit thể hiện dấu được thêm vào bên trái, do đó dấu của số được giữ nguyên.

Ví dụ dưới đây sử dụng thanh ghi 8-bit:

 00010111 (số thập phân +23) Dịch chuyển trái= 00101110 (số thập phân +46)
 10010111 (số thập phân -105) Dịch chuyển phải= 11001011 (số thập phân -53)

Trường hợp đầu tiên, những số tận cùng bên trái được dịch chuyển khỏi thanh ghi, một số 0 mới được thêm vào cuối bên phải của thanh ghi. Trường hợp thứ hai, thành phần cuối bên phải đã được dịch chuyển ra khỏi, và số 1 được thêm vào bên trái, bảo toàn được dấu của số. Nhiều lần dịch chuyển có thể được rút ngắn lại còn một lần. Ví dụ:

 00010111 (số thập phân +23) Dịch sang trái 2 lần.= 01011100 (số thập phân +92)

Dịch chuyển số học bên trái n lần tương đương nhân với 2n (nếu giá trị đó không gây tràn bộ nhớ), trong khi đó thì phép dịch chuyển số học sang phải n lần của một giá trị bù 2 thì tương đương với việc chia cho 2n và làm tròn về phía âm vô cùng. Nếu số nhị phân được xem là bù 1, thì phép dịch chuyển sang phải tương tự sẽ cho kết quả bằng với việc chia số đó cho 2n và làm tròn về phía 0.

Dịch chuyển luận lý

Left logical shiftRight logical shift

Trong dịch chuyển luận lý, các số 0 sẽ được dịch chuyển vào để thay thế các bit bị loại bỏ. Do đó dịch chuyển luận lý và dịch chuyển số học bên trái là hoàn toàn giống nhau.

Tuy nhiên, dịch chuyển luận lý thêm giá trị 0 vào vị trí bit quan trọng nhất, thay vì sao chép bit mang dấu, điều này khá lý tưởng cho các số nhị phân không dấu, trong khi phép dịch chuyển số học sang phải thì lại lý tưởng cho các số nhị phân bù 2 có dấu.

Quay không nhớ

Quay tráiQuay phải

Một dạng khác của dịch chuyển được gọi là dịch chuyển vòng hay quay bit. Với phép toán này, các bit được xoay giống như là hai đầu của thanh ghi được gộp lại với nhau. Những giá trị được dịch chuyển vào ở bên phải trong một lần dịch chuyển trái chính là bất kỳ giá trị nào đã được dịch chuyển ra ở bên trái, và ngược lại. Thao tác này hữu ích nếu xảy ra yêu cầu giữ lại toàn bộ bit hiện thời, và thường được sử dụng trong mật mã học kỹ thuật số.

Quay có nhớ

Quay có nhớ tương tự với phép quay không nhớ, nhưng hai đầu của thanh ghi được tách ra bởi cờ nhớ (carry flag). Bit được dịch chuyển vào (ở bất kỳ đầu nào) là giá trị cũ của cờ nhớ, và bit được dịch chuyển ra (ở đầu còn lại) trở thành giá trị mới của cờ nhớ.

Một phép quay có nhớ có thể mô phỏng một phép quay luận lý hoặc số học của một vị trí bằng cách thiết lập cờ nhớ trước tiên. Ví dụ, nếu cờ nhớ mang giá trị 0, thì x XOAY-PHẢI-CÓ-NHỚ-MỘT-LẦN là phép dịch chuyển luận lý sang phải, và nếu cờ nhớ giữ giá trị của bản sao chép của bit chứa dấu, thì x XOAY-PHẢI-CÓ-NHỚ-MỘT-LẦNlà phép dịch chuyển số học sang phải. Vì lý do này, một số vi điều khiển như các PIC tầm thấp chỉ có xoay và xoay có nhớ, mà không cần đến các cấu trúc dịch chuyển số học và luận lý.

Dịch chuyển trong C, C++, C# và Python

Trong các ngôn ngữ dựa trên C, các toán tử dịch chuyển trái và phải lần lượt là <<>>. Số lượng cần dịch chuyển được cung cấp ở đối số thứ hai của toán tử dịch chuyển. Ví dụ:

x = y << 2;

gán cho x kết quả của phép dịch chuyển y sang trái 2 bit, tương đương với phép nhân với 4.

Trong ngôn ngữ C, kết quả của việc dịch chuyển sang phải một giá trị âm là xác định, và giá trị của phép dịch chuyển sang trái của giá trị chứa dấu là không xác định nếu kết quả không được thể hiện dưới dạng của kết quả. Trong C#, phép dịch chuyển sang phải là một phép dịch chuyển số học khi mà toán hạng là biến kiểu int hoặc long. Nếu toán hạng đầu tiên thuộc kiểu uint hoặc ulong, phép dịch chuyển sang phải là phép dịch chuyển luận lý.

Dịch chuyển trong Java

Trong Java, tất cả các giá trị mang kiểu số nguyên đều có dấu, và các toán tử  <<>> thực hiện các phép dịch chuyển số học. Java còn thêm vào toán tử >>> để thực hiện phép dịch chuyển luận lý sang phải, nhưng bởi vì phép dịch chuyển sang trái số học và luận lý là như nhau, nên không có toán tử <<< trong Java.

Một vài chi tiết về các toán tử dịch chuyển trong Java:

  • Thao tác  << (dịch trái), >> (dịch phải có dấu), và >>> (dịch phải không dấu) được gọi là các toán tử dịch chuyển.
  • Kiểu giá trị mà phép dịch bit biểu thị là dạng cao cấp của toán hạng bên trái. Ví dụ, aByte >>> 2 thì tương đương với ((int) aByte) >>> 2.
  • Nếu như kiểu giá trị cao cấp của toán hạng bên trái là int, thì chỉ có năm bit thấp nhất theo thứ tự của toán hạng bên phải được sử dụng như là khoảng cách dịch chuyển. Điều này giống như là toán hạng bên phải được sử dụng cho một toán tử luận lý thao tác bit AND & với giá trị che đậy 0x1f (0b11111). Khoảng cách dịch chuyển thực ra luôn nằm trong khoảng từ 0 tới 31, một cách bao quát.
  • Nếu như kiểu giá trị cao cấp của toán hạng bên trái là long, thì chỉ có sáu bit thấp nhất theo tứ tự của toán hạng bên phải được sử dụng như là khoảng cách dịch chuyển. Điều đó giống như là toán hạng bên phải được sử dụng cho một toán tử luận lý thao tác bit AND & với giá trị che đậy 0x3f (0b111111). Khoảng cách dịch chuyển thực ra luôn nằm trong khoảng từ 0 tới 63, một cách bao quát.
  • Kết quả của n >>> s là n bị dịch chuyển sang phải s bit và đệm 0 vào bên trái tương ứng.
  • Trong toán tử nói chung và phép dịch bit nói riêng, kiểu dữ liệu  byte được hàm ý chuyển thành int. Nếu giá trị byte đó là âm, và bit bậc cao nhất là một, thì các số một sẽ được điền vào để lấp đầy các bytes được thêm vào ở kiểu int. Do đó byte b1=-5; int i = b1 | 0x0200; sẽ cho kết quả i == -5.

Dịch chuyển trong Pascal

Trong Pascal, cũng như các trình biên dịch tương tự nó (như là Object Pascal và Standard Pascal), các thao tác dịch trái và dịch phải lần lượt là shlshr. Khoảng cách dịch chuyển sẽ được thêm vào trong đối số thứ hai. Ví dụ, câu lệnh sau cho x là kết quả của phép dịch y sang trái hai bit:

x:= y shl 2;